﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace FileCopy.Common
{
    public static class Clipboard
    {
        /// <summary>
        /// Sets the text on the clipboard. Ensures the clipboard is updated before returning.
        /// </summary>
        /// <param name="text">The text.</param>
        public static void SetText(string text)
        {
            // Send the text to the clipboard and ensure it is set because sometimes it doesn't work.
            do
            {
                try
                {
                    Stopwatch stopwatch = new Stopwatch();
                    stopwatch.Start();

#if (DEBUG)
                    Console.WriteLine(String.Format("  Setting clipboard. Time: {1}ms. Text: {0}", text.Length < 30 ? text : String.Format("length is {0}", text.Length), stopwatch.ElapsedMilliseconds));
#endif

                    System.Windows.Forms.Clipboard.SetText(text);

#if (DEBUG)
                    Console.WriteLine(String.Format("  Clipboard set. Time: {0}ms.", stopwatch.ElapsedMilliseconds));
#endif

                    // Check the clipboard is set.
                    int checkCount = 0;
                    do
                    {
                        checkCount++;
                        if (String.Compare(GetText(), text.ToString()) == 0)
                        {
#if (DEBUG)
                            Console.WriteLine(String.Format("  Clipboard set confirmed. Time: {0}ms.", stopwatch.ElapsedMilliseconds));
#endif
                            return;
                        }

#if (DEBUG)
                        Console.WriteLine(String.Format("  Clipboard not updated yet. Time: {0}ms.", stopwatch.ElapsedMilliseconds));
#endif
                        Thread.Sleep(20);
                    }
                    while (checkCount < 10);

#if (DEBUG)
                    Console.WriteLine(String.Format("  Clipboard not updated. Trying to set it again. Time: {0}ms.", stopwatch.ElapsedMilliseconds));
#endif
                }
                catch (ExternalException)
                {
#if (DEBUG)
                    Console.WriteLine("Clipboard locked.");
#endif
                    Thread.Sleep(200);
                }
            }
            while (true);
        }

        /// <summary>
        /// Gets the text from the clipboard.
        /// </summary>
        /// <returns></returns>
        public static string GetText()
        {
            do
            {
                try
                {
                    return System.Windows.Forms.Clipboard.GetText();
                }
                catch (ExternalException)
                {
#if (DEBUG)
                    Console.WriteLine("Clipboard locked.");
#endif
                    Thread.Sleep(200);
                }
            }
            while (true);
        }

        /// <summary>
        /// Waits until the clipboard data changes.
        /// </summary>
        /// <returns>The new clipboard data.</returns>
        public static string WaitForClipboardToChange(string intitalData, int timeoutMilliseconds)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            string currentData;
            do
            {
#if (DEBUG)
                Console.WriteLine(String.Format("  Waiting for clipboard to change. Time: {1}ms. From: {0}", String.IsNullOrEmpty(intitalData) ? "<empty>" : intitalData.Length < 50 ? intitalData : String.Format("<length>{0}", intitalData.Length), stopwatch.ElapsedMilliseconds));
#endif

#if (DEBUG)
                Console.WriteLine(String.Format("  Reading clipboard. Time: {0}ms", stopwatch.ElapsedMilliseconds));
#endif

                currentData = Clipboard.GetText();

#if (DEBUG)
                Console.WriteLine(String.Format("  Clipboard read. Time: {0}ms", stopwatch.ElapsedMilliseconds));
#endif

                // Wait up to an extra five seconds if the clipboard is blank because it may be getting set.
                if (String.IsNullOrEmpty(currentData))
                {
                    Stopwatch stopWatch = new Stopwatch();
                    stopWatch.Start();
                    do
                    {
#if (DEBUG)
                        Console.WriteLine(String.Format("  Clipboard empty. Waiting. Time: {0}ms", stopwatch.ElapsedMilliseconds));
#endif
                        Thread.Sleep(1000);
                        currentData = Clipboard.GetText();
                        if (!String.IsNullOrEmpty(currentData))
                        {
#if (DEBUG)
                            Console.WriteLine(String.Format("  Clipboard set while waiting. Time: {0}ms.", stopwatch.ElapsedMilliseconds));
#endif
                            break;
                        }
                    }
                    while (stopWatch.ElapsedMilliseconds < 5000);
                }

                if (String.Compare(currentData, intitalData) != 0)
                {
#if (DEBUG)
                    Console.WriteLine(String.Format("  Clipboard changed. Time: {1}ms. Data: {0}", currentData.Length < 50 ? currentData : String.Format("<length>{0}", currentData.Length), stopwatch.ElapsedMilliseconds));
#endif

                    return currentData;
                }

                if (timeoutMilliseconds > 0 && stopwatch.ElapsedMilliseconds >= timeoutMilliseconds)
                {
#if (DEBUG)
                    Console.WriteLine(String.Format("  Clipboard not changed. Timeout reached. Time: {0}ms.", stopwatch.ElapsedMilliseconds));
#endif

                    return null;
                }

                Thread.Sleep(500);
            }
            while (true);
        }
    }
}
